﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using StationUpdate;
using System.IO;
using StationUpdate.Enty;
using System.Windows.Forms;

namespace StationUpdate
{
    public class SocketOp
    {
        #region 参数
        private OpType _type = OpType.Client;//默认为客户端
        /// <summary>
        /// 获取或设置通信类型（默认为客户端）
        /// </summary>
        public OpType Type
        {
            get
            {
                return _type;
            }
            set
            {
                _type = value;
            }
        }

        private string _ip = "127.0.0.1";//默认为127.0.0.1
        /// <summary>
        /// 获取或设置IP（默认为127.0.0.1）
        /// </summary>
        public string IP
        {
            get
            {
                return _ip;
            }
            set
            {
                _ip = value;
            }
        }

        private int _port = 4444;//默认为4444
        /// <summary>
        /// 获取或设置端口（默认为4444）
        /// </summary>
        public int Port
        {
            get
            {
                return _port;
            }
            set
            {
                _port = value;
            }
        }

        private AddressFamily _addressfamily = AddressFamily.InterNetwork;//默认为ipv4
        /// <summary>
        /// 获取或设置寻址方案（默认为ipv4）
        /// </summary>
        public AddressFamily AddressFamily
        {
            get
            {
                return _addressfamily;
            }
            set
            {
                _addressfamily = value;
            }
        }

        private SocketType _socketType = SocketType.Stream;//默认为双向无边界
        /// <summary>
        /// 获取或设置套接字类型（默认为双向无边界）
        /// </summary>
        public SocketType SocketType
        {
            get
            {
                return _socketType;
            }
            set
            {
                _socketType = value;
            }
        }

        private ProtocolType _protocoltype = ProtocolType.Tcp;//默认为Tcp
        /// <summary>
        /// 获取或设置协议类型（默认为Tcp）
        /// </summary>
        public ProtocolType ProtocolType
        {
            get
            {
                return _protocoltype;
            }
            set
            {
                _protocoltype = value;
            }
        }

        private int _listencount = 5;//默认为5
        /// <summary>
        /// 获取或设置监听个数（默认为5）
        /// </summary>
        public int LisentCount
        {
            get
            {
                return _listencount;
            }
            set
            {
                _listencount = value;
            }
        }

        private int _resultcount = 1024;
        /// <summary>
        /// 获取或设置接受区域数组大小（默认为1024）
        /// </summary>
        public int ResultCount
        {
            get
            {
                return _resultcount;
            }
            set
            {
                _resultcount = value;
            }

        }

        /// <summary>
        /// Socket
        /// </summary>
        private Socket curSocket = null;
        /// <summary>
        /// 接收区域数组
        /// </summary>
        private byte[] result = null;
        /// <summary>
        /// 错误重试次数
        /// </summary>
        private int errorcount = 3;
        /// <summary>
        /// 响应时间
        /// </summary>
        private int recivetime = 100;
        /// <summary>
        /// 重试时间间隔
        /// </summary>
        private int trytime = 5000;
        #endregion
        LcdStreenApi lcd;
        #region 构造函数
        /// <summary>
        /// 构造函数
        /// </summary>
        public SocketOp()
        {
            lcd = new LcdStreenApi();
            lcd.Url = SysData.webService;

            result = new byte[_resultcount];
        }
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="ip">ip地址</param>
        /// <param name="port">端口号</param>
        public SocketOp(string ip, int port, OpType type = OpType.Client)
        {
            _ip = ip;
            _port = port;
            _type = type;
            result = new byte[_resultcount];
        }
        #endregion

        #region 方法
        /// <summary>
        /// 启动
        /// </summary>
        public void Start()
        {
            try
            {
                
                switch (_type)
                {
                    case OpType.None:
                        break;
                    case OpType.Server: //服务端
                        //绑定ip
                        IPAddress ip = IPAddress.Any;
                        curSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//创建通信
                        curSocket.Bind(new IPEndPoint(ip, _port));//绑定IP地址：端口
                        curSocket.Listen(_listencount);//设定个排队连接请求个数
                        //通过Clientsoket发送数据
                        Thread myThread = new Thread(ListenClientConnect);
                        myThread.Start();
                        break;
                    case OpType.Client://客户端
                        curSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//创建通信
                        //通过Clientsoket发送数据
                        Thread serverthread = new Thread(ConnectServer);
                        serverthread.Start();
                        break;
                    default:
                        break;
                }
            }
            catch (Exception ex)
            {
                SysLog.WriteTxtLog("初始化失败，详细：" + ex.Message);
            }
        }
        /// <summary>
        /// 连接服务器
        /// </summary>
        private void ConnectServer()
        {
            try
            {
                IPAddress ip = IPAddress.Parse(_ip);
                curSocket.Connect(new IPEndPoint(ip, _port)); //配置服务器IP与端口
                Thread.Sleep(recivetime);
                //int length = curSocket.Receive(result);
                byte[] result = null;
                int length = 0;
                ReceiveData(curSocket, ref length, ref result);
                if (length > 0)
                {
                    SysLog.WriteTxtLog("客户端连接成功");
                }
            }
            catch (Exception ex)
            {
                SysLog.WriteTxtLog("连接失败,详细：" + ex.Message);
                Thread.Sleep(trytime);
                Start();
                return;
            }
        }

        /// <summary>
        /// 监听客户端连接(服务端)
        /// </summary>
        private void ListenClientConnect()
        {
            try
            {
                //getSjEntyById("20190823");
                while (true)
                {
                    if (SysData.serverOpen) 
                    {
                        Socket clientSocket = curSocket.Accept();
                        Thread receiveThread = new Thread(ReceiveMessage);
                        receiveThread.Start(clientSocket);    
                    }
                    
                    Thread.Sleep(200);
                }
            }
            catch (Exception ex)
            {
                SysLog.WriteTxtLog("服务端监听异常，详细：" + ex.Message);
            }

        }

        /// <summary>
        /// 接收消息（服务端）
        /// </summary>
        /// <param name="clientSocket"></param>
        private void ReceiveMessage(object clientSocket)
        {
            Socket myClientSocket = (Socket)clientSocket;

            try
            {
                while (true)
                {
                    Thread.Sleep(100);

                    if (!SysData.serverOpen)
                    {
                        break;
                    }

                    //通过clientSocket接收数据
                    //int receiveNumber = myClientSocket.Receive(result);
                    int receiveNumber = 0;
                    ReceiveData(myClientSocket, ref receiveNumber, ref result);

                    StringBuilder builder = new StringBuilder();
                    builder.Append(CommonUtil.ConvertByteTo16Str(result));


                    if (receiveNumber <= 0)
                    {
                        continue;
                    }

                    SysLog.WriteTxtLog("收到数据:" + builder.ToString());

                    if (string.IsNullOrEmpty(builder.ToString()))
                    {
                        //忽略该包
                        continue;
                    }
                    else if (builder.ToString().Length <= 18)
                    {//最小包长度
                        continue;
                    }
                    else if (!builder.ToString().StartsWith("XXXX"))
                    {//包头非法
                        continue;
                    }
                    else
                    {
                        //CRC校验
                        int len = CommonUtil.Convert16To10(builder.ToString().Substring(4, 2*2)) * 2;
                        if (builder.ToString().Length < len)
                        {//长度非法
                            continue;
                        }
                        Crc16 crc16 = new Crc16();
                        string str = builder.ToString().Substring(0, len);

                        string strcrc = crc16.getCrc16(str).ToString("X").PadLeft(4, '0');
                        if (strcrc != "0000")
                        {
                            continue;
                        }

                        //协议解析部分，不再公开，大家可基于自己协议发挥
                        int clintSoftVer = 0;
                        string lcdNo = "XX";
                        string type = "XX";

                        SjEnty sjEnty = getSjEntyById(lcdNo); 
                        
                        if (sjEnty == null)
                        {
                            SysLog.WriteTxtLog("stationId=["+ lcdNo + "]获取到的升级版本信息为空，关闭连接");
                            myClientSocket.Close();
                            return;
                        }
                        //判断是否需要升级
                        if (sjEnty.version == "xxxx")
                        {
                            SysLog.WriteTxtLog("stationId=[" + lcdNo + "]终端版本与服务端版本一致，关闭连接");
                            myClientSocket.Close();
                            return;
                        }

                        //去看磁盘是否有，如果没有，则忽略本次循环，同时将文件放到下载列表中，
                        string fileName = sjEnty.filePath.Substring(sjEnty.filePath.LastIndexOf("/")+1);
                        SjFileInfo sjFileInfo = SysData.listSjFileInfo.Find(it=>it.sjFileName==fileName);
                        if (sjFileInfo == null)
                        {
                            sjFileInfo = new SjFileInfo();
                            sjFileInfo.isComple = false;
                            sjFileInfo.sjFileName = fileName;
                            sjFileInfo.sjLocalFileName = Application.StartupPath + "\\updateFile\\" + fileName;
                            sjFileInfo.sjServerFileName = SysData.webService + sjEnty.filePath;
                            //添加到下载列表中
                            SysData.listSjFileInfo.Add(sjFileInfo);

                            Thread.Sleep(6000);
                            continue;
                        }
                        if (!sjFileInfo.isComple)
                        {
                            //正在下载，等待
                            Thread.Sleep(6000);
                            continue;
                        }

                        //包的大小
                        int packetSize = Convert.ToInt32(SysData.OneSendSize);
                        //包的数量
                        int packetCount = (int)(sjFileInfo.EzoneFile.Length / ((long)packetSize));
                        //最后一个包的大小
                        int lastPacketData = (int)(sjFileInfo.EzoneFile.Length % packetSize);
                        byte[] data = new byte[packetSize];
                        int curPackIndex = 0;//当前已升级了的包序号

                        if (lastPacketData > 0)
                        {
                            packetCount++;
                        }

                        //存在内存中，则直接获取对应的包数，发送。

                        TermStation curTermStation;
                        lock (SysData.objSyn)
                        {
                            if (!SysData.dicTermStation.ContainsKey(lcdNo))
                            {
                                curTermStation = new TermStation();
                                curTermStation.TermId = lcdNo;
                                SysData.dicTermStation.Add(lcdNo, curTermStation);
                            }
                            SysData.dicTermStation.TryGetValue(lcdNo, out curTermStation);
                        }

                        

                        //if (clintSoftVer != SysData.SoftVer && type== "C6" && !curTermStation.IsUpdated)
                        //{
                        //    curTermStation.IsUpdated = true;
                        //    //发送升级请求
                        //    string sendRequestStr = getSendRequestStr(stationId, packetCount);
                        //    SendData(myClientSocket, sendRequestStr);
                        //    SysLog.WriteTxtLog("向【" + stationId + "】发送请求升级数据:" + sendRequestStr);
                        //}

                        //if (clintSoftVer != SysData.SoftVer && type == "FE" && curTermStation.IsUpdated)
                        if (clintSoftVer.ToString() != sjEnty.version && type.ToUpper() == "XX")
                        {
                            //升级请求包
                            //获取集中控制器所需要的第几包
                            int clientRequestPakcNum = CommonUtil.Convert16To10(str.Substring(58, 4));//包序号
                            curPackIndex = clientRequestPakcNum;

                            byte[] heByte = new byte[packetSize];
                            sjFileInfo.EzoneStream.Seek(curPackIndex * packetSize, SeekOrigin.Begin);
                            int r = sjFileInfo.EzoneStream.Read(heByte, 0, heByte.Length);

                            string sendStr = CommonUtil.ConvertByteTo16Str(heByte);

                            sendStr = getSendStr(lcdNo, curPackIndex, packetCount, sendStr, sjEnty.version);

                            SendData(myClientSocket, sendStr);

                            SysLog.WriteTxtLog("向ID:"+ lcdNo + " 发送第【"+ clientRequestPakcNum + "】包数据:" + sendStr);

                            curTermStation.CurUpdateSize = clientRequestPakcNum + 1;

                            

                            sjFileInfo.lastUseTime = DateTime.Now;
                        }

                        curTermStation.FileSize = packetCount;
                        curTermStation.TermId = lcdNo;
                        curTermStation.HardWareVer = clintSoftVer.ToString();
                        curTermStation.SoftVer = clintSoftVer.ToString();
                        curTermStation.LastUpdateTime = DateTime.Now;

                    }

                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                myClientSocket.Shutdown(SocketShutdown.Both);
                myClientSocket.Close();
                SysLog.WriteTxtLog("接收失败,详细：" + ex.Message);
            }
            
        }

        private string getSendStr(string stationId, int curPackIndex, int packetCount, string senStr, string softVer) 
        {
            Crc16 crc16 = new Crc16();
            StringBuilder sb = new StringBuilder();
            //拼装向设备发送升级文件的协议，此处大家自己发挥

            string crcS = crc16.getCrc16(sb.ToString()).ToString("X").PadLeft(4, '0');
            sb.Append(crcS);
            return sb.ToString();
        }


        /// <summary>
        /// 发送消息（客户端）
        /// </summary>
        /// <param name="msg">消息内容</param>
        /// <returns></returns>
        public bool SendData(string msg)
        {
            //非客户端直接退出
            if (_type != OpType.Client)
            {
                return true;
            }
            if (curSocket == null)
            {
                return false;
            }
            bool ret = false;
            try
            {
                StringBuilder sb = new StringBuilder(msg);
                AddBodyHead(ref sb, "BBCC");
                msg = sb.ToString();

                //curSocket.Send(Encoding.ASCII.GetBytes(msg));
                SendData(curSocket, msg);
                Thread.Sleep(recivetime);
                //int length = curSocket.Receive(result);
                int length=0;
                ReceiveData(curSocket, ref length, ref result);
                string res = "";
                if (length > 0)
                {
                    res = Encoding.ASCII.GetString(result, 0, length);
                }
                List<string> list = FenBao(res);
                for (int i = 0; i < list.Count; i++)
                {
                    res = ChaiBao(list[i], "BBCC");
                    if (res == "ok")
                    {
                        ret = true;
                    }
                }
            }
            catch (Exception ex)
            {
                curSocket.Shutdown(SocketShutdown.Both);
                curSocket.Close();
                SysLog.WriteTxtLog("发送失败,详细：" + ex.Message);
                ConnectServer();
                errorcount = 3;
            }
            return ret;
        }

        private int SendData(Socket curSk, string str)
        {
            int write = 0;
            try
            {
                byte[] buf = CommonUtil.convertStringToCharArray(str);
                //{
                //如果网络上有数据,先清除缓冲区剩余无效数据
                //if (_socketClient.Available > 0)
                //{
                //    byte[] _recvBuf = new byte[1024];
                //    int read = _socketClient.Receive(_recvBuf);//,0,buf.Length ,SocketFlags .None ); 
                //}
                //pool 单位是微秒 ,等待10毫秒
                if (curSk.Poll(1000 * 60, System.Net.Sockets.SelectMode.SelectWrite))
                {
                    write = curSk.Send(buf);//,0,buf.Length ,SocketFlags.None);
                }
                //}
            }
            catch (Exception ex)
            {
                throw ex;
            }
            return write;
        }

        private void ReceiveData(Socket curSk, ref int receiveLength, ref byte[] retByte)
        {
            int read = 0;

            List<byte[]> list = new List<byte[]>();
            try
            {
                while (curSk.Available > 0)
                {
                    byte[] bufTmp = new byte[1024];
                    read = curSk.Receive(bufTmp, bufTmp.Length, SocketFlags.None);
                    list.Add(bufTmp);
                    Thread.Sleep(10);
                    receiveLength = receiveLength + read;
                }

                if (receiveLength <= 0)
                {
                    //没收到数据
                    return;
                }

                retByte = new byte[receiveLength];
                byte[] retTmpByte = new byte[list.Count * 1024];
                #region 将list数据转移致buf中
                int curRead = 0;
                for (int i = 0; i < list.Count; i++)
                {
                    Buffer.BlockCopy(list[i], 0, retTmpByte, curRead, list[0].Length);
                    curRead = curRead + list[i].Length;
                }

                Buffer.BlockCopy(retTmpByte, 0, retByte, 0, receiveLength);


                #endregion
                //}
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        /// <summary>
        /// 处理包头，长度，CRC
        /// </summary>
        /// <param name="sb">传入：包体内容，</param>
        public void AddBodyHead(ref StringBuilder sb, string head)
        {
            //报文格式
            //客户端->服务器：BBCC（2个字节）长度（2个字节）包体CRC（2个字节）
            //服务器->客户端：CCBB（2个字节）长度（2个字节）包体CRC（2个字节）
            if (sb == null || sb.Length <= 0) 
            {
                return;
            }
            if (string.IsNullOrEmpty(head)
                || (!head.ToUpper().Equals("BBCC") && !head.ToUpper().Equals("CCBB"))
               ) 
            {
                throw new Exception("包头非法，包头内容为：" + head);
            }

            //包体内容转成16进制串
            sb = new StringBuilder(CommonUtil.ConvertStringToISN(sb.ToString()));

            int len = sb.Length / 2 + 6;
            sb.Insert(0, CommonUtil.Convert10To16(len).PadLeft(4, '0'));
            sb.Insert(0, head.ToUpper());
            Crc16 crc16 = new Crc16();
            string strcrc = crc16.getCrc16(sb.ToString()).ToString("X").PadLeft(4, '0');
            sb.Append(strcrc);
        }

        public string ChaiBao(string str, string head) 
        {
            string ret="";
            int headLength = 0;
            if (str == null ||
                str.Length <= 0) 
            {
                return ret;
            }
            if (!str.ToUpper().StartsWith(head.ToUpper())) 
            {
                return ret;
            }
            if (str.Length < 12) 
            {
                return ret;
            }
            headLength = CommonUtil.Convert16To10(str.Substring(4, 4));
            if (str.Length != headLength * 2) 
            {
                return ret;
            }

            Crc16 crc16 = new Crc16();
            string strcrc = crc16.getCrc16(str).ToString("X").PadLeft(4, '0');
            if (strcrc != "0000") 
            {
                return ret;
            }
            //包体内容转成源串
            ret = CommonUtil.Convert16ToChinase(str.Substring(8, str.Length - 8 - 4));

            return ret;
        }

        public List<string> FenBao(string source)
        {
            List<string> list = new List<string>();
            if (string.IsNullOrEmpty(source)) 
            {
                return list;
            }
            while (source.Length >= 18)
            {//最小包的长度
                int oneLength = 0;
                if (source.ToUpper().StartsWith("B66B")) 
                {
                    oneLength = CommonUtil.Convert16To10(source.Substring(4, 4))*2;
                    list.Add(source.Substring(0,oneLength));
                }
                source = source.Substring(oneLength);
            }

            return list;
        }

        #endregion

        public SjEnty getSjEntyById(string lcdNo)
        {
            SjEnty sj = null;
            try
            {
                string str = lcd.getUpGrade(lcdNo);
                sj = JsonUtil.JsonStringToObj<SjEnty>(str);
               
            }
            catch (Exception ex)
            {
                SysLog.WriteTxtLog("根据设备id：[" + lcdNo + "]获取升级版本信息异常：" + ex.Message);
            }
            return sj;
        }
        public static void CheckLogFilePath(string strPath)
        {
            try
            {
                DirectoryInfo dir = new DirectoryInfo(strPath);
                if (!dir.Exists)
                {
                    // 如果文件夹不存在，那么就创建
                    dir.Create();
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.ToString());
            }
        }
        
    }

    public enum OpType
    {
        [Description("不启用")]
        None = 0,
        [Description("服务端")]
        Server = 1,
        [Description("客户端")]
        Client = 2
    }


  

}
